#include "../libc/string.h"
#include "../libc/mem.h"
#include "../libc/list.h"
#include "../cpu/port.h"
#include "../driver/screen.h"
#include "pci.h"

list_t *pci_list;

static uint32_t pci_read(uint8_t bus, uint8_t dev, uint8_t fun, uint8_t offset) {
    uint32_t addr = 0x80000000 | ((uint32_t) bus << 16) 
        | ((uint32_t) dev << 11) 
        | ((uint32_t) fun << 8)
        | (offset & 0xFC);
    port_dword_out(CONFIG_ADDR, addr);
    return port_dword_in(CONFIG_DATA);
}

void pci_enumerate() {
    pci_list = list_create();
    char str[20];
    int i,j,k;
    for (i = 0; i < BUS_COUNT; i++)
    {
        for (j = 0; j < DEV_COUNT; j++)
        {
            for (k = 0; k < FUN_COUNT; k++)
            {
                uint32_t id = pci_read(i, j, k, 0);
                if(id == 0xffffffff) continue;
                uint16_t device = id >> 16;
                uint16_t vendor = id & 0xffff;
                uint32_t cls = pci_read(i, j, k, 0x08);
                uint8_t cls_code = (uint8_t)((cls >> 24) & 0xff);
                uint8_t sub_cls = (uint8_t)((cls >> 16) & 0xff);
                uint8_t proif = (uint8_t)((cls >> 8) & 0xff);
                uint32_t bar0_org =  pci_read(i, j, k, 0x10);
                uint32_t bar0 = bar0_org & ~0xful;

                pci_dev_t *d = kmalloc(sizeof(pci_dev_t), 0);
                d->bus = i;
                d->dev = j;
                d->func = k;
                d->vendor = vendor;
                d->device = device;
                d->class_code = cls_code;
                d->sub_class = sub_cls;
                d->proif = proif;
                d->bar[0] = bar0;
                list_add(pci_list, d);

                kprint("vendor: ");
                int2hex(vendor, str);
                kprint(str);
                kprint(", device: ");
                int2hex(device, str);
                kprint(str);
                kprint(", cls_code: ");
                int2hex(cls_code, str);
                kprint(str);
                kprint(", sub_cls: ");
                int2hex(sub_cls, str);
                kprint(str);
                kprint(", proif: ");
                int2hex(proif, str);
                kprint(str);
                kprint(", bar0 org: ");
                int2hex(bar0_org, str);
                kprint(str);
                kprint("\n");
            }
        }
    }
    
}